﻿// *********************************************************************
// [DCOM Productions]
// [Copyright (C) DCOM Productions All rights reserved.]
// *********************************************************************

namespace BitFlex.Windows.Forms {
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Security.Permissions;
    using System.Runtime.InteropServices;
    using System.Threading;

    #region ExplorerProgressBar

    /// <summary>
    /// The Windows Explorer ProgressBar
    /// </summary>
    [ToolboxBitmap(typeof(ProgressBar))]
    public sealed partial class ExplorerProgressBar : System.Windows.Forms.ProgressBar {
        /// <summary>
        /// Instantiates a new instance of the BitFlex.Windows.Forms.ExplorerProgressBar class
        /// </summary>
        public ExplorerProgressBar() {
            InitializeComponent();
        }

        #region Platform Invoke

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern IntPtr SendMessage(IntPtr hWnd, Int32 msg, IntPtr wParam, IntPtr lParam);

        #endregion

        #region Constants

        private const Int32 PBM_SETSTATE        = WM_USER + 16;
        private const Int32 PBS_SMOOTHREVERSE   = 0x10;
        private const Int32 PBS_VERTICAL        = 0x04;
        private const Int32 PBST_ERROR          = 0x0002;
        private const Int32 PBST_NORMAL         = 0x0001;
        private const Int32 PBST_PAUSED         = 0x0003;
        private const Int32 WM_USER             = 0x0400;

        #endregion

        /// <summary>
        /// Encapsulates the information needed when creating a control
        /// </summary>
        protected override CreateParams CreateParams {
            [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
            get {
                CreateParams cp = base.CreateParams;
                if (SmoothReverse)
                    cp.Style = cp.Style | PBS_SMOOTHREVERSE;
                else
                    cp.Style &= ~PBS_SMOOTHREVERSE;
                if (Vertical)
                    cp.Style = cp.Style | PBS_VERTICAL;
                else
                    cp.Style &= ~PBS_VERTICAL;
                return cp;
            }
        }

        /// <summary>
        /// Overrides the base OnPaint method
        /// </summary>
        protected override void OnPaint(PaintEventArgs e) {
            SetState();
            base.OnPaint(e);
        }

        /// <summary>
        /// Overrides the base window procedure
        /// </summary>
        /// <param name="m"></param>
        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
        protected override void WndProc(ref Message m) {
            if (m.Msg == 15) {
                SetState();
            }
            base.WndProc(ref m);
        }

        #region Properties

        private bool m_SmoothReverse = false;
        /// <summary>
        /// Gets or sets a System.Boolean value indicating whether the SmoothReverse style is used
        /// </summary>
        [Category("Behavior")]
        [DefaultValue(false)]
        [Description("Gets or sets a System.Boolean value indicating whether the SmoothReverse style is used")]
        public bool SmoothReverse {
            get {
                return m_SmoothReverse;
            }
            set {
                m_SmoothReverse = value;
                UpdateStyles();
            }
        }

        private ExplorerProgressBarState m_State = ExplorerProgressBarState.Normal;
        /// <summary>
        /// Gets or Sets the state of the progress bar
        /// </summary>
        /// <remarks>
        /// A 500ms delay occurrs when setting this property to ensure the progress bar
        /// visual appearance is properly updated before another line of code is executed.
        /// This keeps the visual appearance in sync with other UI updates, however, you must
        /// call the setter of this property before any updates to keep it in sync.
        /// </remarks>
        [
            Category("Behavior"),
            DefaultValue(typeof(ExplorerProgressBarState), "Normal"),
            Description("Gets or Sets the state of the progress bar")
        ]
        public ExplorerProgressBarState State {
            get {
                return m_State;
            }
            set {
                m_State = value;
                SetState();
                // Small delay that allows this to update before other lines of code are executed
                // Without this you would notice the progress bar takes about 500ms to actually
                // Change its visual appearance to match the state
                if (StateDelay) {
                    for (int interval = 0; interval < 500; interval++) {
                        Application.DoEvents();
                        Thread.Sleep(1);
                    }
                }
            }
        }

        private bool m_StateDelay = false;
        /// <summary>
        /// Gets or sets a System.Boolean value indicating whether a 500ms delay will be emplaced when <see cref="State"/> property is changed to keep the visual appearance synchronized
        /// </summary>
        [
            Category("Behavior"),
            DefaultValue(false),
            Description("Gets or sets a System.Boolean value indicating whether a 500ms delay will be emplaced when State property is changed to keep the visual appearance synchronized")
        ]
        public bool StateDelay {
            get {
                return m_StateDelay;
            }
            set {
                m_StateDelay = value;
            }
        }

        private bool m_Vertical = false;
        /// <summary>
        /// Gets or sets a System.Boolean value indicating whether the progress bar will be rendered vertically
        /// </summary>
        [Category("Behavior")]
        [DefaultValue(false)]
        [Description("Gets or sets a System.Boolean value indicating whether the progress bar will be rendered vertically")]
        public bool Vertical {
            get {
                return m_Vertical;
            }
            set {
                m_Vertical = value;
                UpdateStyles();
            }
        }

        #endregion

        #region Methods

        private void SetState() {
            if (!IsDisposed) {
                SendMessage(Handle, PBM_SETSTATE, new IntPtr((Int32)ExplorerProgressBarState.Normal), IntPtr.Zero);
                switch (State) {
                    case ExplorerProgressBarState.Normal:
                        SendMessage(Handle, PBM_SETSTATE, new IntPtr(PBST_NORMAL), IntPtr.Zero);
                        break;
                    case ExplorerProgressBarState.Paused:
                        SendMessage(Handle, PBM_SETSTATE, new IntPtr(PBST_PAUSED), IntPtr.Zero);
                        break;
                    case ExplorerProgressBarState.Error:
                        SendMessage(Handle, PBM_SETSTATE, new IntPtr(PBST_ERROR), IntPtr.Zero);
                        break;
                    default:
                        SendMessage(Handle, PBM_SETSTATE, new IntPtr(PBST_NORMAL), IntPtr.Zero);
                        break;
                }
            }
        }

        #endregion
    }

    #endregion
}